Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utils: Spread objects in cloneSceneObjectState #967

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Conversation

kaydelaney
Copy link
Contributor

@kaydelaney kaydelaney commented Nov 14, 2024

Fixes an issue where if a panel was duplicated, both the original panel and duplicated panel's queries state (in SceneQueryRunner) referred to the same object in memory, meaning when the duplicate query was changed, so was the original.

Thread: https://raintank-corp.slack.com/archives/C03KVDHTWAH/p1730897481961219

📦 Published PR as canary version: 5.37.1--canary.967.12828986565.0

✨ Test out this PR locally via:

npm install @grafana/[email protected]
npm install @grafana/[email protected]
# or 
yarn add @grafana/[email protected]
yarn add @grafana/[email protected]

@kaydelaney kaydelaney added type/bug Something isn't working patch Increment the patch version when merged release Create a release when this pr is merged labels Nov 14, 2024
@kaydelaney kaydelaney self-assigned this Nov 14, 2024
@torkelo
Copy link
Member

torkelo commented Nov 14, 2024

where do we mutate the queries object?

@kaydelaney
Copy link
Contributor Author

@torkelo It might be something strange some datasources do - could only reproduce with graphite/Mock datasource but not prometheus for example. I'll check.

@kaydelaney
Copy link
Contributor Author

Okay so looking into a bit more, at least in the case of Graphite, I think what's happening is it's taking the target that's passed to it and ultimately manipulating that when the query is modified via the graphite query editor.

https://github.com/grafana/grafana/blob/6abe99efd64b8867112d9d9c74971d96840ce32d/public/app/plugins/datasource/graphite/state/context.tsx#L91

Changing the above to target: { ...query } also seems to fix the issue, but other data sources also seem to make the same mistake, so it might be worthwhile "fixing" in scenes just the same.

Comment on lines 48 to 49
} else if (typeof child === 'object') {
newArray.push({ ...child });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to be problematic for properties that are Array. The individual array values will be spread as properties on the state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch. Do you think something like this would be okay?

Suggested change
} else if (typeof child === 'object') {
newArray.push({ ...child });
} else if (typeof child === 'object' && !Array.isArray(child)) {
newArray.push({ ...child });

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, that could work.

But if it's just graphite maybe we can fix the mutation here: https://github.com/grafana/grafana/blob/6abe99efd64b8867112d9d9c74971d96840ce32d/public/app/plugins/datasource/graphite/state/store.ts#L53

(and elsewhere)

@leeoniya
Copy link
Contributor

leeoniya commented Jan 2, 2025

this bug was also reported for Google Cloud Monitoring datasource: grafana/grafana#97223 (comment)

Copy link
Member

@axelavargas axelavargas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @kaydelaney 👋🏾 , as other mentioned, can we fix this issue in the problematic data sources that are doing the mutation? There might be more side effects on implementing this approach

@oscarkilhed
Copy link
Contributor

Is there really more side effects to implementing this approach? If I were to clone a panel I wouldn't expect properties on it to be referring to the same property as the old panel, I think this has a great potential to cause more bugs in the future.

That said, the mutation in the graphite DS is also not great and should probably be fixed too.

@kaydelaney kaydelaney requested a review from dprokop January 17, 2025 12:23
@@ -45,6 +45,8 @@ export function cloneSceneObjectState<TState extends SceneObjectState>(
for (const child of propValue) {
if (child instanceof SceneObjectBase) {
newArray.push(child.clone());
} else if (typeof child === 'object' && !Array.isArray(child)) {
newArray.push({ ...child });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An issue just popped up around transformations that is kind of related to this PR. Basically, duplicating a panel with some transformation and then modifying the transformation options within the second panel would also modify the transformation options in the initial panel.

This happens because of the spread operator won't cover objects that contain deeper nested arrays, which is the case for transformations where you'd have an object of the form:

{
   id: "convertFieldType",
   options: {
       conversions: [{ ...some objects in array }]
   }
}

Modifying this line to use lodash cloneDeep instead of spread op fixes the issue with transformations, so maybe we can use that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info! I worry that using cloneDeep might introduce some performance issues considering how often scene objects are cloned, but I'd need to benchmark it to be sure. I'll look into it :)

Copy link
Collaborator

@mdvictor mdvictor Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I share your worry, thought about it myself, not sure how to deal with it tho. Some benchmarking would be great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
patch Increment the patch version when merged release Create a release when this pr is merged type/bug Something isn't working
Projects
Status: 🗂️ Needs Triage / Escalation
Development

Successfully merging this pull request may close these issues.

7 participants